
`include "common_header.verilog"

//  *************************************************************************
//  File : p8264_top_tx_40g.v
//  *************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized reproduction or use is expressly prohibited.
//  Copyright (c) 2009 MorethanIP
//  Muenchner Strasse 199, 85757 Karlsfeld, Germany
//  info@morethanip.com
//  http://www.morethanip.com
//  *************************************************************************
//  Designed by : Denis Poverennyy
//  info@morethanip.com
//  *************************************************************************
//  Description : Complete 40G PCS: Transmit Top Level Structure
//  Version     : $Id: p8264_top_tx_40g.v,v 1.3 2014/08/08 13:30:04 wt Exp $
//  *************************************************************************

module p8264_top_tx_40g (

        reset_txclk,
        reset_sd_tx_clk,
        cgmii_txclk,
        sd_tx_clk,
        sd_tx_clk_ena,
        cgmii_txc,
        cgmii_txd,
        cgmii_txclk_ena,
        sd0_tx,
        sd1_tx,
        sd2_tx,
        sd3_tx,

   `ifdef MTIPPCS_FEC_ENA
   
        fec_ena,

   `endif

   `ifdef MTIPPCS82_EEE_ENA 
   
        lpi_tick,
        lpi_fw,
        scr_bypass_enable,
        tx_lpi_mode,
        tx_lpi_state,
        tx_lpi_indication,
   
   `endif
   
        sw_reset,
        vl_intvl,       
        vl_0_enc,
        vl_1_enc,
        vl_2_enc,
        vl_3_enc,        
        tx_en_gen_pat,
        tx_lane_thresh);

`include "mtip_pcs100g_pack_package.verilog"

input   reset_txclk;                    //  async active high reset
input   [3:0] reset_sd_tx_clk;          //  async active high reset
input   cgmii_txclk;                    //  system ref clock >260.4MHz clock
input   [3:0] sd_tx_clk;                //  serdes clock clock
input   [3:0] sd_tx_clk_ena;            //  Serdes Tx clock enable
input   [7:0] cgmii_txc;                //  XL/CGMII transmit control
input   [63:0] cgmii_txd;               //  XL/CGMII transmit data
output  cgmii_txclk_ena;                //  XL/CGMII transmit clock enable
output  [SERDES_WIDTH-1:0] sd0_tx;      //  data 0 output
output  [SERDES_WIDTH-1:0] sd1_tx;      //  data 1 output
output  [SERDES_WIDTH-1:0] sd2_tx;      //  data 2 output
output  [SERDES_WIDTH-1:0] sd3_tx;      //  data 3 output
input   sw_reset;                       //  Software Reset
input   [15:0] vl_intvl;                //  AM Period control (if 1,then test mode)
input   [23:0] vl_0_enc;                //  Marker pattern for PCS Virtual Lane 0
input   [23:0] vl_1_enc;                //  Marker pattern for PCS Virtual Lane 1
input   [23:0] vl_2_enc;                //  Marker pattern for PCS Virtual Lane 2
input   [23:0] vl_3_enc;                //  Marker pattern for PCS Virtual Lane 3
input   tx_en_gen_pat;                  //  enable test pattern generator
input   [3:0] tx_lane_thresh;           //  4 bit per lane tx decoupling buffer level threshold (5..7 depending on refclk)

`ifdef MTIPPCS_FEC_ENA

input   [3:0]           fec_ena;                //  FEC Enable per lane

`endif

`ifdef MTIPPCS82_EEE_ENA 

input  		lpi_tick;		// A timer tick, which asserts for one reference clock (ref_clk) cycle in average every 100ns.
                                        // Ten ticks represent the duration of 1us.
input           lpi_fw;                 // Boolean variable controlling the wake mode for the LPI transmit and receive functions. This
                                        // variable is set true when the link is to use the fast wake mechanism, and false when the link is to
                                        // use the optional deep sleep mechanism for each direction. This variable defaults true and may only
                                        // be set to false if the optional deep sleep mode is supported.
input           scr_bypass_enable;      // A Boolean variable used to indicate to the transmit LPI state diagram that the scrambler bypass
                                        // option is required. The PHY shall set scr_bypass_enable = TRUE if Clause 74 FEC is in use. The
                                        // PHY shall set scr_bypass_enable = FALSE if this FEC is not in use.
                                 
output  [1:0]   tx_lpi_mode;            // A variable reflecting state of the LPI transmit function as described by the LPI transmit state
                                        // diagram (Figure 82-16). When tx_mode is set to QUIET the sublayer may go into a low power

output  [2:0]   tx_lpi_state;           // A variable reflecting state of the LPI SM as described by the LPI transmit state  diagram (Figure 82-16).
output          tx_lpi_indication;      //  status 1[9]:  Tx LPI indication A Boolean variable indicating the current state of the transmit LPI function. 
                                        //  This flag is set to true when the LPI transmit state diagram is in any state other than TX_ACTIVE.
 
`endif

wire    cgmii_txclk_ena;
wire    [SERDES_WIDTH-1:0] sd0_tx;         
wire    [SERDES_WIDTH-1:0] sd1_tx;         
wire    [SERDES_WIDTH-1:0] sd2_tx;         
wire    [SERDES_WIDTH-1:0] sd3_tx;         

wire    [65:0]  tx_mld_d0;
wire    [65:0]  tx_mld_d1;
wire    [65:0]  tx_mld_d2;
wire    [65:0]  tx_mld_d3;

`ifdef MTIPPCS82_EEE_ENA 

 wire   [1:0]   tx_lpi_mode;  
 wire   [2:0]   tx_lpi_state;      
 wire           tx_lpi_indication; 

`endif

// Internal Signals
//-----------------

wire    mld_rd;                 //  read to the MLD (clock enable for MLD and FEC)
wire    gb_rd;                  //  read from the gearbox
wire    [3:0] sw_reset_v;       //  SW Reset Control
wire    [3:0] tx_bf_afull;      //  line buffer almost full (backpressure)
wire    [3:0] tx_bf_full;       //  line buffer full (overflow/underflow error)
wire    [3:0] tx_bf_full_d;     //  line buffer full delayed
wire    sw_reset_wclk_v;        //  write side in ref_clk
wire    bf_afull_any;           //  any of the 4 buffers
wire    [3:0] tx_bf_full_q;     //  Qualified Buffer Full Indication
wire    bf_full_any;            //  Buffer Full (Or'ed)
reg     [1:0] mld_ena_cnt;      //  MLD Enable Cycle Counter

`ifdef MTIPPCS_FEC_ENA

 wire    [3:0] fec_ena_s; //  FEC Enable (system clock)

 //  ---------------------
 //  sync async inputs
 //  ---------------------

 mtip_xsync #(4) U_SYFEC (
 
          .data_in(fec_ena),
          .reset(reset_txclk),
          .clk(cgmii_txclk),
          .data_s(fec_ena_s));

`endif

// ENCODER + SCRAMBLER + Optional EEE82 + MLD
// ------------------------------------------

p8264_pcs_mld_tx U_PCS_MLD_TX (

        .reset_txclk            (reset_txclk),
        .cgmii_txclk            (cgmii_txclk),
        .sw_reset               (sw_reset_wclk_v),
        .cgmii_txc              (cgmii_txc),
        .cgmii_txd              (cgmii_txd),
        .cgmii_txclk_ena        (cgmii_txclk_ena),
        .cgmii_tx_tsu           (),
        .scr_bypass             (1'b 0),
        .disable_mld            (1'b 0),
        .mode25                 (1'b 0),
        .en_gen_pat49           (1'b 0),
        .sel_pat                (1'b 0),
        .seed_1                 (58'd 0),
        .seed_2                 (58'd 0),
        .marker_ins_cnt         (),
        .vl_intvl               (vl_intvl),       
        .vl_0_enc               (vl_0_enc),
        .vl_1_enc               (vl_1_enc),
        .vl_2_enc               (vl_2_enc),
        .vl_3_enc               (vl_3_enc),        
        .tx_en_gen_pat          (tx_en_gen_pat),
        
   `ifdef MTIPPCS82_EEE_ENA 
        
        .lpi_tick               (lpi_tick),
        .lpi_fw                 (lpi_fw),
        .scr_bypass_enable      (scr_bypass_enable),
        .tx_lpi_mode            (tx_lpi_mode),
        .tx_lpi_state           (tx_lpi_state),
        .tx_lpi_indication      (tx_lpi_indication),
   
   `endif
   
        .tx_mld_d0              (tx_mld_d0),        
        .tx_mld_d1              (tx_mld_d1),        
        .tx_mld_d2              (tx_mld_d2),        
        .tx_mld_d3              (tx_mld_d3),
        .mld_rd                 (mld_rd), 
        .marker_start           ());

//  Gearboxes
//  ---------

pcs_tx_lane U_TXLANE0 (

          .reset_txclk(reset_txclk),
          .reset_sd_tx_clk(reset_sd_tx_clk[0]),
          .txclk(cgmii_txclk),
          .sd_tx_clk(sd_tx_clk[0]),
          .sd_tx_clk_ena(sd_tx_clk_ena[0]),
          .tx_d(tx_mld_d0),
          .tx_d_wr(mld_rd),
          .tx_bf_afull(tx_bf_afull[0]),
          .tx_bf_full(tx_bf_full[0]),
          .sd_tx(sd0_tx),
        
        `ifdef MTIPPCS_FEC_ENA
        
          .fec_ena(fec_ena_s[0]),
        
        `endif
        
          .gb_bp_ena(1'b 0),
          .sw_reset(sw_reset_v[0]),
          .sw_reset_wclk(sw_reset_wclk_v),
          .sw_reset_rclk(),
          .tx_lane_thresh(tx_lane_thresh));

pcs_tx_lane U_TXLANE1 (

          .reset_txclk(reset_txclk),
          .reset_sd_tx_clk(reset_sd_tx_clk[1]),
          .txclk(cgmii_txclk),
          .sd_tx_clk(sd_tx_clk[1]),
          .sd_tx_clk_ena(sd_tx_clk_ena[1]),
          .tx_d(tx_mld_d1),
          .tx_d_wr(mld_rd),
          .tx_bf_afull(tx_bf_afull[1]),
          .tx_bf_full(tx_bf_full[1]),
          .sd_tx(sd1_tx),
        
        `ifdef MTIPPCS_FEC_ENA
        
          .fec_ena(fec_ena_s[1]),
        
        `endif
        
          .gb_bp_ena(1'b 0),
          .sw_reset(sw_reset_v[1]),
          .sw_reset_wclk(),
          .sw_reset_rclk(),
          .tx_lane_thresh(tx_lane_thresh));

pcs_tx_lane U_TXLANE2 (

          .reset_txclk(reset_txclk),
          .reset_sd_tx_clk(reset_sd_tx_clk[2]),
          .txclk(cgmii_txclk),
          .sd_tx_clk(sd_tx_clk[2]),
          .sd_tx_clk_ena(sd_tx_clk_ena[2]),
          .tx_d(tx_mld_d2),
          .tx_d_wr(mld_rd),
          .tx_bf_afull(tx_bf_afull[2]),
          .tx_bf_full(tx_bf_full[2]),
          .sd_tx(sd2_tx),
        
        `ifdef MTIPPCS_FEC_ENA
        
          .fec_ena(fec_ena_s[2]),
        
        `endif
        
          .gb_bp_ena(1'b 0),
          .sw_reset(sw_reset_v[2]),
          .sw_reset_wclk(),
          .sw_reset_rclk(),
          .tx_lane_thresh(tx_lane_thresh));

pcs_tx_lane U_TXLANE3 (

          .reset_txclk(reset_txclk),
          .reset_sd_tx_clk(reset_sd_tx_clk[3]),
          .txclk(cgmii_txclk),
          .sd_tx_clk(sd_tx_clk[3]),
          .sd_tx_clk_ena(sd_tx_clk_ena[3]),
          .tx_d(tx_mld_d3),
          .tx_d_wr(mld_rd),
          .tx_bf_afull(tx_bf_afull[3]),
          .tx_bf_full(tx_bf_full[3]),
          .sd_tx(sd3_tx),
        
        `ifdef MTIPPCS_FEC_ENA
        
          .fec_ena(fec_ena_s[3]),
        
        `endif
        
          .gb_bp_ena(1'b 0),
          .sw_reset(sw_reset_v[3]),
          .sw_reset_wclk(),
          .sw_reset_rclk(),
          .tx_lane_thresh(tx_lane_thresh));
      
assign bf_afull_any = |(tx_bf_afull);

//  MLD Enable
//  ----------

always@(posedge reset_txclk or posedge cgmii_txclk)
   begin
    if (reset_txclk==1'b 1)
       begin
       mld_ena_cnt <= 2'b 00;
       end
    else
       begin
       if (sw_reset_wclk_v==1'b 1)
          begin
          mld_ena_cnt <= 2'b 00;
          end
       else
          begin
          if (mld_ena_cnt==2'b 11 & bf_afull_any==1'b 0)
             begin
             mld_ena_cnt <= 2'b 00;
             end
          else if (mld_ena_cnt!=2'b 11)
             begin
             mld_ena_cnt <= mld_ena_cnt+2'd 1;
             end
          end
       end
   end
   
assign mld_rd = (mld_ena_cnt==2'b 11 & bf_afull_any==1'b 0) ? 1'b 1 : 1'b 0;

// ---------------------
// Sync reset control
// Issue a soft reset to all Lane buffers if it is either
// triggered by PCS control register, or if any FIFO overflow occurs
// (indicating e.g. a Serdes clock switchoff)
//
// Buffers will reset also when no specific mode is configured but the buffers run full,
// to clean up any configuration change, or serdes clock change/switch off.
// ---------------------

mtip_dffvec #(4) U_FULLDFF (

          .reset(reset_txclk),
          .clk(cgmii_txclk),
          .i(tx_bf_full),
          .o(tx_bf_full_d));

assign tx_bf_full_q = tx_bf_full & tx_bf_full_d; //  qualify, must be stable for 2 consecutive cycles (absolute!)
assign bf_full_any  = |(tx_bf_full_q);
assign sw_reset_v   = bf_full_any == 1'b 1 | sw_reset == 1'b 1 ? 4'b 1111 : 4'b 0000;

endmodule